#include <linux/err.h>
#include <linux/types.h>

#include <asm/thread_info.h>
#include <asm/ptrace.h>

inline long do_mmap2(
	unsigned long addr, unsigned long len,
	unsigned long prot, unsigned long flags,
	unsigned long fd, unsigned long pgoff)
{
	int error = -EINVAL;
	struct file * file = NULL;

	fd = prot;

	printf("this is %s(): %d >>> fd = %d\r\n", __func__, __LINE__, fd);

	// flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);

	// if (flags & MAP_FIXED && addr < FIRST_USER_ADDRESS)
		// goto out;

	error = -EBADF;
	// if (!(flags & MAP_ANONYMOUS)) {
	if (fd != -1) {
		file = fget(fd);
		if (!file)
			goto out;
	}

	printf("this is %s(): %d\r\n", __func__, __LINE__);
	// down_write(&current->mm->mmap_sem);
	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
	// up_write(&current->mm->mmap_sem);

	// if (file)
		// fput(file);
out:
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return error;
}

int sys_fork(struct pt_regs *regs)
{
#if 1
	unsigned long stack = (unsigned long)kmalloc(THREAD_SIZE);
	memcpy((void *)stack, (void *)regs->ARM_sp - THREAD_SIZE/2, THREAD_SIZE/2);
	memcpy((void *)stack + THREAD_SIZE/2, (void *)regs->ARM_sp, THREAD_SIZE/2);
	return do_fork(0, stack + THREAD_SIZE/2, regs, 0, NULL, NULL);
#else
	return do_fork(0, regs->ARM_sp, regs, 0, NULL, NULL);
#endif
}

void sys_execve(char *filename, char **argv, char **envp, struct pt_regs *regs)
{
	int i;
	char *k_argv = (char *)argv;
	char *kernel_filename = kmalloc(strlen(filename));
	// int argc = count(argv, 0x7FFFFFFF);
	char *kernel_argv = kmalloc(strlen(k_argv));

	strcpy(kernel_filename, filename, strlen(filename));

	// printf("this is %s(): %d >>> argc = %d\r\n", __func__, __LINE__, argc);
	printf("this is %s(): %d >>> filename = %x\r\n", __func__, __LINE__, filename);
	printf("this is %s(): %d >>> argv = %x\r\n", __func__, __LINE__, k_argv);
	printf("this is %s(): %d >>> argv = %s\r\n", __func__, __LINE__, k_argv);
	// printf("this is %s(): %d >>> argv[0][0] = %c\r\n", __func__, __LINE__, argv[0][0]);
	strcpy(kernel_argv, k_argv, strlen(k_argv));

	// for (i = 0; i < 3; i++) {
		// strcpy(kernel_argv[i], argv[i], strlen(argv[i]));
		// printf("this is %s(): %d >>> kernel_argv[%d] = %s\r\n", __func__, __LINE__, i, kernel_argv[i]);
		// printf("this is %s(): %d >>> argv[%d] = %s\r\n", __func__, __LINE__, i, argv[i]);
	// }

    do_execve(kernel_filename, kernel_argv, 0, regs);
}

int kernel_execve(const char *filename, char *const argv[], char *const envp[])
{
	struct pt_regs regs;
	int ret;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	memset(&regs, 0, sizeof(struct pt_regs));
	ret = do_execve((char *)filename, (char **)argv, (char **)envp, &regs);
	if (ret < 0)
		goto out;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	/*
	 * Save argc to the register structure for userspace.
	 */
	regs.ARM_r0 = ret;

	/*
	 * We were successful.  We won't be returning to our caller, but
	 * instead to user space by manipulating the kernel stack.
	 */
	asm(	"add	r0, %0, %1\n\t"
		"mov	r1, %2\n\t"
		"mov	r2, %3\n\t"
		"bl	memmove\n\t"	/* copy regs to top of stack */
		"mov	r8, #0\n\t"	/* not a syscall */
		"mov	r9, %0\n\t"	/* thread structure */
		"mov	sp, r0\n\t"	/* reposition stack pointer */
		"b	ret_to_user"
		:
		: "r" (current_thread_info()),
		  "Ir" (THREAD_START_SP - sizeof(regs)),
		  "r" (&regs),
		  "Ir" (sizeof(regs))
		: "r0", "r1", "r2", "r3", "ip", "lr", "memory");

	printf("this is %s(): %d\r\n", __func__, __LINE__);

 out:
	return ret;
}
